#include <iostream>
using namespace std;

/*
    C++模板：
        (1) 函数模板
        (2) 类模板
*/

namespace FUNC_DAY15
{
    // 函数模板
    template <typename T>
    T ADD(T a, T b)
    {
        return (a+b);
    }

    template < class T,class U>
    class A
    {
        private:
            T t1;
            T t2;
            U name;
        public:
            A(T a, T b): t1(a), t2(b) {cout << "有参构造函数" << endl;}
            T add()
            {
                return (t1+t2);
            }
            T sub();

            void print(U str)
            {
                cout << str << endl;
            }

    };

    // 类模板成员函数外部定义
    template < class T,class U1>
    T A<T,U1>::sub()
    {
        return (A<T,U1>::t1 - A<T,U1>::t2);
    }
};


// 非模板友元函数，也就是说友元函数不是模板类型
namespace CLASS1_DAY15
{
    template<typename T>
    class Person
    {
        T x;
        public:
            Person(const T& t)
            {
                x = t;
            }
            friend void log(const Person<T>& a);//有参友元函数
    };
    void log(const Person<int>& a)//模板形参为int类型（显示具体化）
    {
        cout << "int:" << a.x << endl;
    }
    void log(const Person<double>& a)//模板形参为double类型（显示具体化）
    {
        cout << "double:" << a.x << endl;
    }

};

namespace CLASS2_DAY15
{
    using namespace std;
    //（1）函数模板声明
    template<typename T>
    void func();
    template<typename T>
    void show(T& t);
    //类模板定义
    template <typename U>
    class A
    {
        private:
            U item;
            static int count;
        public:
            A(const U& u) :item(u) { count++; }
            ~A() { count--; }
            //（2）在类模板中将函数模板声明为类的友元函数
            friend void func<U>();   //友元函数模板
            friend void show<>(A<U>& a);   //友元函数模板
    };
    template<typename T>
    int A<T>::count = 0;     //类A的T类型对象的个数
    //（3）友元函数模板的定义
    template<typename T>
    void func()
    {
        cout << "template size: " << sizeof(A<T>) << ";";
        cout << " template func(): " << A<T>::count << endl;
    }
    template<typename T>
    void show(T& t)
    {
        cout << t.item << endl;
    }
};

namespace CLASS3_DAY15
{
    template<typename T>
    class Person
    {
        private:
            T item;
        public:
            Person(const T& t) :item(t) {}
            template<typename U, typename V>  //在类内部声明函数模板
            friend void show(U& u, V& v);
    };
    template<typename U, typename V>
    void show(U& u, V& v)
    {
        cout << u.item << "," << v.item << endl;
    }  
};



int main (int argc, char *argv[])
{
    {
        using namespace FUNC_DAY15;
        cout << "\n\n";
        cout << "*****************函数模板*****************" << endl;
        cout << "\n\n";
        cout << "a+b: " << ADD<float>(12.3,35) << endl;

        cout << "a+b: " << ADD(12,34) << endl;

        cout << "a+b: " << ADD(12.3,34.0) << endl;
        
        // 函数模板的类型推导必须一致
        // cout << "a+b: " << ADD(12,34.4) << endl;

        cout << "\n\n";
        cout << "*****************类模板成员函数外部定义*****************" << endl;
        cout << "\n\n";
        A<int,string> a1(23,45);
        A<int,string> a2(23,45.9);
        cout << "a1->a+b: " << a1.add() << endl;
        cout << "a1->a-b: " << a1.sub() << endl;
        cout << "a2->a+b: " << a2.add() << endl;

        a1.print("Hello Gui Yang Yang!");

    }

    {
        using namespace CLASS1_DAY15;
        cout << "\n\n";
        cout << "*****************非模板友元函数*****************" << endl;
        cout << "\n\n";
        Person<int> a(10);//创建int类型对象
        log(a);
        Person<double> b(12.5);//创建double类型对象
        log(b);


    }

    {
        using namespace CLASS2_DAY15;
        func<int>();    //调用int类型的函数模板实例，int类型，其大小为	4字节
        A<int> a(10);   //定义类对象
        A<int> b(20);
        A<double> c(1.2);
        cout << "\n\n";
        cout << "*****************约束模板友元函数*****************" << endl;
        cout << "\n\n";
        show(a);   //调用show()函数，输出类对象的数据成员值
        show(b);
        show(c);
        cout << "func<int> output:\n";
        func<int>();    //运行到此，已经创建了两个int类型对象
        cout << "func<double>() output:\n";
        func<double>();
    }

    {
        using namespace CLASS3_DAY15;
        cout << "\n\n";
        cout << "*****************非约束模板友元函数*****************" << endl;
        cout << "\n\n";
        Person<int> a(10);
        Person<int> b(20);
        Person<double> c(1.2);
        cout << "a,b: ";
        show(a, b);
        cout << "a,c: ";
        show(a, c);
    }
    
    return 0;
}

 